#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>
#include "Client.h"
#include "ICommand.h"
#include "SwitchOnCommand.h"
#include "SwitchOffCommand.h"
#include "MacroCommand.h"
#include "CDSlotOpen.h"
#include "CDSlotClose.h"
#include "Stereo.h"
#include "Heating.h"
#include "TV.h"

void Client::AddDevice(IDevice * dev){
	if (dev != 0){
		//push back a device to the list
		mDevices.push_back(dev);
	}
	else{
		std::exception("Client::AddDevice: invalid pointer");
	}
}

void Client::ProgramRemoteControl(){
	//devices +  commands for them
	//stereo device -> a MacroCommand is built up
	//that CDSlot is opened at turning on and closed at turning off
	Stereo * stereo = new Stereo(); 
	ICommand * cmdOnStereo = new SwitchOnCommand(stereo);
	ICommand * cmdOffStereo = new SwitchOffCommand(stereo);
	ICommand * cmdOpenSlotStereo = new CDSlotOpen(stereo);
	ICommand * cmdCloseSlotStereo = new CDSlotClose(stereo);
	MacroCommand * onStereo = new MacroCommand();
	MacroCommand * offStereo = new MacroCommand();
	onStereo->AddCommand(cmdOnStereo);
	onStereo->AddCommand(cmdOpenSlotStereo);
	offStereo->AddCommand(cmdCloseSlotStereo);
	offStereo->AddCommand(cmdOffStereo);
	//heating device -> able to be switched off and on
	Heating * heating = new Heating();
	ICommand * cmdOnHeating = new SwitchOnCommand(heating);
	ICommand * cmdOffHeating = new SwitchOffCommand(heating);
	//TV device -> able to be switched off and on
	TV * tv = new TV(); 
	ICommand * cmdOnTV = new SwitchOnCommand(tv);
	ICommand * cmdOffTV = new SwitchOffCommand(tv);

	//Add devices to the Clients list
	AddDevice(tv);
	AddDevice(heating);
	AddDevice(stereo);

	//Add more devices that are unused
	Stereo * stereo2 = new Stereo();
	Stereo * stereo3 = new Stereo();
	Heating * heating2 = new Heating();
	TV * tv2 = new TV();
	AddDevice(tv2);
	AddDevice(heating2);
	AddDevice(stereo2);
	AddDevice(stereo3);

	//Add commands to the Command list
	AddCommand(cmdOnStereo);
	AddCommand(cmdOffStereo);
	AddCommand(cmdOpenSlotStereo);
	AddCommand(cmdCloseSlotStereo);
	AddCommand(onStereo);
	AddCommand(offStereo);
	AddCommand(cmdOnHeating);
	AddCommand(cmdOffHeating);
	AddCommand(cmdOnTV);
	AddCommand(cmdOffTV);

	//program the devices on slots of the remote control
	mRemoteControl.ProgramSlot(1, cmdOnTV, cmdOffTV, "Samsung TV");
	mRemoteControl.ProgramSlot(3, cmdOnHeating, cmdOffHeating, "Heating");
	mRemoteControl.ProgramSlot(4, onStereo, offStereo, "Bose Stereo");
}

void Client::UseRemoteControl(){
	std::string str = "";
	//end usage when input is "e"
	while (str != "e"){
		//print interactive menu
		PrintInterface();
		//ask for input from std::cin
		std::getline(std::cin, str);
		//print information when input is "i"
		if (str == "i"){
			std::cout << std::endl << "devices:" << std::endl;
			std::cout << "----------------------" << std::endl;
			//print information of all devices (all states)
			std::for_each(mDevices.begin(), mDevices.end(), PrintDevice);
		}
		//change status of a device
		else if (str.size() == 2){
			//first input must be a number
			if (isdigit(str[0])){
				//second input is the status (switch on when "o")
				if (str[1] == 'o'){
					mRemoteControl.OnButtonPressed(atoi(str.c_str()));
				}
				//switch off when "f"
				else if (str[1] == 'f'){
					mRemoteControl.OffButtonPressed(atoi(str.c_str()));
				}
				//input is wrong
				else{
					std::cout << "Wrong input" << std::endl;
				}
			}
		}
		//undo last action when input is "u"
		else if (str == "u"){
			mRemoteControl.UndoButtonPressed();
		}
		else{
			//when input is none of the known, wrong input happend
			if (str != "e"){
				std::cout << "Wrong input" << std::endl;
			}
		}
		std::cout << std::endl;
	}
	//free all allocated memory
	FreeLists();
}

void Client::FreeLists(){
	for_each(mDevices.begin(), mDevices.end(), DeleteDevice);
	for_each(mCommands.begin(), mCommands.end(), DeleteCommand);
}

void Client::DeleteDevice(IDevice * dev){
	if (dev != 0){
		delete dev; dev = 0;
	}
}

void Client::DeleteCommand(ICommand * com){
	if (com != 0){
		delete com; com = 0;
	}
}

void Client::AddCommand(ICommand * com){
	if (com != 0){
		//push back a command to the list
		mCommands.push_back(com);
	}
	else{
		std::exception("Client::AddCommand: invalid pointer");
	}
}

void Client::PrintInterface(){
	//print interactive menu
	std::cout << "remote control:" << std::endl;
	std::cout << "----------------------" << std::endl;
	//print the slot layout (slot numbers with the corresponding devices
	for (size_t i = 1; i < mRemoteControl.GetNrOfSlots(); ++i){
		std::cout << i << "..." << mRemoteControl.GetDeviceName(i) << std::endl;
	}

	std::cout << "u...undo" << std::endl;
	std::cout << "i...output device info" << std::endl;
	std::cout << "----------------------" << std::endl;
	std::cout << "input slot number and on('o') or off('f'): ";
}

void Client::PrintDevice(IDevice * dev){
	if (dev != 0){
		//print information to std::cout
		dev->Info(std::cout);
	}
}